﻿using System;
using System.Collections.Generic;
using System.IO;
using fastCSharp;
using fastCSharp.threading;
using System.Threading;

namespace diantou.examAnswerCacheServer
{
    /// <summary>
    /// 考试答案缓存服务
    /// </summary>
    [fastCSharp.code.cSharp.tcpServer(Service = "ExamAnswerCache", IsIdentityCommand = true, IsServerAsynchronousReceive = false, VerifyMethodType = typeof(server.tcpClient.timeVerifyMethod))]
    public partial class server : fastCSharp.net.tcp.timeVerifyServer
    {
        /// <summary>
        /// 用户考试答案+会话标识
        /// </summary>
        private struct answerSession
        {
            /// <summary>
            /// 用户考试答案
            /// </summary>
            public Dictionary<fastCSharp.sql.randomKey<diantou.dataModel.examQuestion.questionType>, diantou.dataModel.examAnswerBase> Answers;
            /// <summary>
            /// 用户会话变化通知
            /// </summary>
            public Func<fastCSharp.net.returnValue<int>, bool> OnChange;
            /// <summary>
            /// 测试日志问题标识集合
            /// </summary>
            public subArray<int> LogProblemIds;
            /// <summary>
            /// 会话标识
            /// </summary>
            public int SessionId;
            /// <summary>
            /// 跨域验证码
            /// </summary>
            public uint VerifyCode;
            /// <summary>
            /// 是否存在日志
            /// </summary>
            public bool IsLog;
            /// <summary>
            /// 会话标识
            /// </summary>
            /// <param name="verifyCode"></param>
            /// <returns></returns>
            public int NextSessionId(out uint verifyCode, ref Func<fastCSharp.net.returnValue<int>, bool> onChange)
            {

                if (SessionId == -1)
                {
                    verifyCode = 0;
                    return 0;
                }
                onChange = OnChange;
                verifyCode = VerifyCode = fastCSharp.random.Default.SecureNextUIntNotZero();
                OnChange = null;
                return ++SessionId;
            }
            /// <summary>
            /// 会话标识
            /// </summary>
            /// <param name="verifyCode"></param>
            /// <returns></returns>
            public int NewSessionId(out uint verifyCode)
            {
                verifyCode = VerifyCode = fastCSharp.random.Default.SecureNextUIntNotZero();
                return 1;
            }
            /// <summary>
            /// 缓存初始化
            /// </summary>
            /// <param name="fileCache"></param>
            public void Set(fileCache fileCache)
            {
                LogProblemIds = new subArray<int>(fileCache.LogProblemIds);
                SessionId = fileCache.SessionId;
                VerifyCode = fileCache.VerifyCode;
                IsLog = fileCache.IsLog;
                if (fileCache.Answers != null) New(fileCache.Answers);
            }
            /// <summary>
            /// 获取用户考试答案
            /// </summary>
            /// <param name="type"></param>
            /// <param name="questionId"></param>
            /// <returns></returns>
            public diantou.dataModel.examAnswerBase Get(dataModel.examQuestion.type type, int questionId)
            {
                if (Answers != null)
                {
                    diantou.dataModel.examAnswerBase answer;
                    Answers.TryGetValue(new dataModel.examQuestion.questionType { Type = type, QuestionId = questionId }, out answer);
                    return answer;
                }
                return null;
            }
            /// <summary>
            /// 新用户添加答案
            /// </summary>
            /// <param name="answer"></param>
            public void New(diantou.dataModel.examAnswerBase answer)
            {
                Answers = new Dictionary<fastCSharp.sql.randomKey<dataModel.examQuestion.questionType>, dataModel.examAnswerBase>();
                Answers[new diantou.dataModel.examQuestion.questionType { QuestionId = answer.QuestionId, Type = answer.Type }] = answer;
            }
            /// <summary>
            /// 新用户添加答案
            /// </summary>
            /// <param name="answers"></param>
            public void New(diantou.dataModel.examAnswerBase[] answers)
            {
                Answers = new Dictionary<fastCSharp.sql.randomKey<dataModel.examQuestion.questionType>, dataModel.examAnswerBase>();
                foreach (diantou.dataModel.examAnswerBase answer in answers) Answers[new diantou.dataModel.examQuestion.questionType { QuestionId = answer.QuestionId, Type = answer.Type }] = answer;
            }
            /// <summary>
            /// 添加答案
            /// </summary>
            /// <param name="answer"></param>
            public void Append(diantou.dataModel.examAnswerBase answer)
            {
                if (Answers == null) Answers = new Dictionary<fastCSharp.sql.randomKey<dataModel.examQuestion.questionType>, dataModel.examAnswerBase>();
                Answers[new diantou.dataModel.examQuestion.questionType { QuestionId = answer.QuestionId, Type = answer.Type }] = answer;
            }
            /// <summary>
            /// 添加答案
            /// </summary>
            /// <param name="answers"></param>
            public void Append(diantou.dataModel.examAnswerBase[] answers)
            {
                if (Answers == null) Answers = new Dictionary<fastCSharp.sql.randomKey<dataModel.examQuestion.questionType>, dataModel.examAnswerBase>();
                foreach (diantou.dataModel.examAnswerBase answer in answers) Answers[new diantou.dataModel.examQuestion.questionType { QuestionId = answer.QuestionId, Type = answer.Type }] = answer;
            }
            /// <summary>
            /// 获取答案
            /// </summary>
            /// <param name="isLog"></param>
            /// <param name="logProblemIds"></param>
            /// <returns></returns>
            public diantou.dataModel.examAnswerBase[] GetAnswer(out bool isLog, out subArray<int> logProblemIds)
            {
                isLog = IsLog;
                logProblemIds = LogProblemIds;
                return Answers == null ? nullValue<diantou.dataModel.examAnswerBase>.Array : Answers.Values.getArray();
            }
            /// <summary>
            /// 设置测试日志问题标识集合
            /// </summary>
            /// <param name="logProblemIds"></param>
            public unsafe void SetLogProblemIds(ref subArray<int> logProblemIds)
            {
                if (LogProblemIds.Count == 0) LogProblemIds = logProblemIds;
                else
                {
                    fixed (int* logFixed = LogProblemIds.UnsafeArray, newLogFixed = logProblemIds.UnsafeArray)
                    {
                        int* read = newLogFixed, readEnd = newLogFixed + logProblemIds.Count, end = logFixed + LogProblemIds.Count;
                        do
                        {
                            int* start = logFixed;
                            do
                            {
                                if (*read == *start) break;
                                if (++start == end)
                                {
                                    LogProblemIds.Add(*read);
                                    break;
                                }
                            }
                            while (true);
                        }
                        while (++read != readEnd);
                    }
                }
            }
            /// <summary>
            /// 跨域验证
            /// </summary>
            /// <param name="sessionId"></param>
            /// <param name="veirfyCode"></param>
            /// <returns>0表示错误,1表示成功,2表示过期</returns>
            public int CheckVerifyCode(int sessionId, uint veirfyCode)
            {
                if (SessionId == sessionId)
                {
                    if (VerifyCode == veirfyCode) return 1;
                    VerifyCode = 0;
                    return 0;
                }
                return 2;
            }
            /// <summary>
            /// 跨域验证
            /// </summary>
            /// <param name="sessionId"></param>
            /// <param name="veirfyCode"></param>
            /// <returns>0表示错误,1表示成功,2表示过期</returns>
            public int CheckVerifyCode(int sessionId, uint veirfyCode, ref bool isLog, ref subArray<int> logProblemIds)
            {
                if (SessionId == sessionId)
                {
                    if (VerifyCode == veirfyCode)
                    {
                        logProblemIds = LogProblemIds;
                        isLog = IsLog;
                        return 1;
                    }
                    VerifyCode = 0;
                    return 0;
                }
                return 2;
            }
            /// <summary>
            /// 获取跨域验证码
            /// </summary>
            /// <param name="sessionId"></param>
            /// <returns></returns>
            public uint GetVerifyCode(int sessionId)
            {
                return SessionId == sessionId ? VerifyCode : 0;
            }
            /// <summary>
            /// 删除答案
            /// </summary>
            public Func<fastCSharp.net.returnValue<int>, bool> Remove()
            {
                Func<fastCSharp.net.returnValue<int>, bool> onChange = OnChange;
                LogProblemIds.Null();
                SessionId = -1;
                Answers = null;
                OnChange = null;
                return onChange;
            }
            /// <summary>
            /// 清空数据状态
            /// </summary>
            public Func<fastCSharp.net.returnValue<int>, bool> Null()
            {
                Func<fastCSharp.net.returnValue<int>, bool> onChange = OnChange;
                LogProblemIds.Null();
                SessionId = 0;
                Answers = null;
                OnChange = null;
                return onChange;
            }
            /// <summary>
            /// 设置用户会话变化通知
            /// </summary>
            /// <param name="sessionId"></param>
            /// <param name="onChange"></param>
            /// <param name="oldOnChange"></param>
            public void SetOnChange(ref int sessionId, ref Func<fastCSharp.net.returnValue<int>, bool> onChange, ref Func<fastCSharp.net.returnValue<int>, bool> oldOnChange)
            {
                if (SessionId == sessionId)
                {
                    oldOnChange = OnChange;
                    OnChange = onChange;
                    onChange = null;
                }
                else sessionId = SessionId;
            }
        }
        /// <summary>
        /// 文件缓存结构
        /// </summary>
        [fastCSharp.emit.dataSerialize(IsReferenceMember = false, IsMemberMap = false)]
        private struct fileCache
        {
            /// <summary>
            /// 测试日志问题标识集合
            /// </summary>
            public int[] LogProblemIds;
            /// <summary>
            /// 考试用户标识
            /// </summary>
            public int UserId;
            /// <summary>
            /// 会话标识
            /// </summary>
            public int SessionId;
            /// <summary>
            /// 跨域验证码
            /// </summary>
            public uint VerifyCode;
            /// <summary>
            /// 是否存在日志
            /// </summary>
            public bool IsLog;
            /// <summary>
            /// 用户考试答案
            /// </summary>
            public diantou.dataModel.examAnswerBase[] Answers;
        }
        /// <summary>
        /// 考试答案缓存
        /// </summary>
        private answerSession[] cache;
        /// <summary>
        /// 考试答案缓存访问锁
        /// </summary>
        private readonly object cacheLock = new object();
        /// <summary>
        /// 考试答案缓存文件名称
        /// </summary>
        private string cacheFileName
        {
            get
            {
                return fastCSharp.config.pub.Default.CachePath + "examAnswerCache.data";
            }
        }
        /// <summary>
        /// 在线用户数量
        /// </summary>
        private int userCount;
        /// <summary>
        /// 在线用户数量
        /// </summary>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private int UserCount
        {
            get { return userCount; }
        }
        /// <summary>
        /// 设置TCP服务端
        /// </summary>
        /// <param name="tcpServer">TCP服务端</param>
        public override void SetCommandServer(fastCSharp.net.tcp.commandServer tcpServer)
        {
            base.SetCommandServer(tcpServer);
            string cacheFile = cacheFileName;
            fileCache[] fileCache = null;
            if (File.Exists(cacheFile))
            {
                try
                {
                    fileCache = fastCSharp.emit.dataDeSerializer.DeSerialize<fileCache[]>(File.ReadAllBytes(cacheFile));
                    if (fileCache == null) log.Error.Add("考试答案缓存反序列化失败 " + cacheFile, new System.Diagnostics.StackFrame(), false);
                    else File.Delete(cacheFile);
                }
                catch (Exception error)
                {
                    log.Error.Add(error, null, false);
                }
            }
            int userId = 255;
            foreach (fileCache answer in fileCache.notNull())
            {
                if (answer.UserId > userId) userId = answer.UserId;
            }
            cache = new answerSession[userId + 1];
            foreach (fileCache answer in fileCache.notNull()) cache[answer.UserId].Set(answer);
            cache[0].SessionId = -1;
        }
        /// <summary>
        /// 获取考试用户会话标识
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="verifyCode"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private int nextSession(int userId, out uint verifyCode)
        {
            Func<fastCSharp.net.returnValue<int>, bool> onChange = null;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length)
            {
                int sessionId = cache[userId].NextSessionId(out verifyCode, ref onChange);
                Monitor.Exit(cacheLock); 
                this.onChange(onChange, sessionId);
                return sessionId;
            }
            try
            {
                create(userId);
                return cache[userId].NewSessionId(out verifyCode);
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 设置考试答案缓存
        /// </summary>
        /// <param name="answer">考试答案</param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void set(diantou.dataModel.examAnswerBase answer)
        {
            Monitor.Enter(cacheLock);
            if (answer.UserId >= cache.Length)
            {
                try
                {
                    create(answer.UserId);
                    cache[answer.UserId].New(answer);
                }
                finally { Monitor.Exit(cacheLock); }
                return;
            }
            if (cache[answer.UserId].SessionId == -1)
            {
                Monitor.Exit(cacheLock); 
                return;
            }
            try
            {
                cache[answer.UserId].Append(answer);
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 新建考试答案缓存
        /// </summary>
        /// <param name="userId">考试用户标识</param>
        private void create(int userId)
        {
            int size = Math.Max(cache.Length << 1, userId + 256);
            answerSession[] newCache = new answerSession[size];
            Array.Copy(cache, newCache, cache.Length);
            cache = newCache;
        }
        /// <summary>
        /// 设置考试答案缓存
        /// </summary>
        /// <param name="userId">考试用户标识</param>
        /// <param name="answers">考试答案集合</param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void set(int userId, diantou.dataModel.examAnswerBase[] answers)
        {
            Monitor.Enter(cacheLock);
            if (userId >= cache.Length)
            {
                try
                {
                    create(userId);
                    cache[userId].New(answers);
                }
                finally { Monitor.Exit(cacheLock); }
                return;
            }
            if (cache[userId].SessionId == -1)
            {
                Monitor.Exit(cacheLock); 
                return;
            }
            try
            {
                cache[userId].Append(answers);
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 获取考试答案缓存用户会话标识集合
        /// </summary>
        /// <returns>考试答案缓存用户会话标识集合</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private keyValue<int, int>[] getSessions()
        {
            int count = 0;
            Monitor.Enter(cacheLock);
            foreach (answerSession answer in cache)
            {
                if (answer.SessionId != -1) ++count;
            }
            if (count == 0)
            {
                Monitor.Exit(cacheLock); 
                return nullValue<keyValue<int, int>>.Array;
            }
            try
            {
                keyValue<int, int>[] values = new keyValue<int, int>[count];
                int userId = 0;
                foreach (answerSession answer in cache)
                {
                    if (answer.SessionId != -1) values[--count].Set(userId, answer.SessionId);
                    ++userId;
                }
                return values;
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 获取考试答案缓存
        /// </summary>
        /// <param name="userId"></param>
        /// <returns>考试答案缓存集合</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private diantou.dataModel.examAnswerBase[] get(int userId)
        {
            Monitor.Enter(cacheLock);
            if (userId >= cache.Length)
            {
                Monitor.Exit(cacheLock); 
                return nullValue<diantou.dataModel.examAnswerBase>.Array;
            }
            try
            {
                return cache[userId].Answers.values().getArray();
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 获取考试答案缓存
        /// </summary>
        /// <param name="userId"></param>
        /// <returns>考试答案缓存集合</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private diantou.dataModel.examAnswerBase[] get(int userId, out bool isLog, out subArray<int> logProblemIds)
        {
            Monitor.Enter(cacheLock);
            if (userId >= cache.Length)
            {
                Monitor.Exit(cacheLock); 
                isLog = false;
                logProblemIds = default(subArray<int>);
                return nullValue<diantou.dataModel.examAnswerBase>.Array;
            }
            try
            {
                return cache[userId].GetAnswer(out isLog, out logProblemIds);
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 获取考试答案
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="type"></param>
        /// <param name="questionId"></param>
        /// <returns>考试答案缓存</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private diantou.dataModel.examAnswerBase get(int userId, dataModel.examQuestion.type type, int questionId)
        {
            Monitor.Enter(cacheLock);
            if (userId >= cache.Length)
            {
                Monitor.Exit(cacheLock); 
                return null;
            }
            try
            {
                return cache[userId].Get(type, questionId);
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 跨域验证
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sessionId"></param>
        /// <param name="verifyCode"></param>
        /// <returns>0表示错误,1表示成功,2表示过期</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private int checkVerifyCode(int userId, int sessionId, uint verifyCode)
        {
            int value = 2;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length) value = cache[userId].CheckVerifyCode(sessionId, verifyCode);
            Monitor.Exit(cacheLock); 
            return value;
        }
        /// <summary>
        /// 跨域验证
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sessionId"></param>
        /// <param name="verifyCode"></param>
        /// <returns>0表示错误,1表示成功,2表示过期</returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private int checkVerifyCode(int userId, int sessionId, uint verifyCode, ref bool isLog, ref subArray<int> logProblemIds)
        {
            int value = 2;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length) value = cache[userId].CheckVerifyCode(sessionId, verifyCode, ref isLog, ref logProblemIds);
            Monitor.Exit(cacheLock); 
            return value;
        }
        /// <summary>
        /// 设置日志状态
        /// </summary>
        /// <param name="userId"></param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void setLog(int userId)
        {
            Monitor.Enter(cacheLock);
            if (userId >= cache.Length)
            {
                try
                {
                    create(userId);
                    cache[userId].IsLog = true;
                }
                finally { Monitor.Exit(cacheLock); }
                return;
            }
            cache[userId].IsLog = true;
            Monitor.Exit(cacheLock); 
        }
        /// <summary>
        /// 测试日志问题标识集合
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="logProblemIds"></param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void setLogProblemIds(int userId, subArray<int> logProblemIds)
        {
            if (logProblemIds.Count != 0)
            {
                Monitor.Enter(cacheLock);
                try
                {
                    if (userId >= cache.Length)
                    {
                        create(userId);
                        cache[userId].LogProblemIds = logProblemIds;
                    }
                    else cache[userId].SetLogProblemIds(ref logProblemIds);
                }
                finally { Monitor.Exit(cacheLock); }
            }
        }
        /// <summary>
        /// 会话过期检测
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sessionId"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private bool checkSessionId(int userId, int sessionId)
        {
            return userId < cache.Length && cache[userId].SessionId == sessionId;
        }
        /// <summary>
        /// 获取跨域验证
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sessionId"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private uint getVerifyCode(int userId, int sessionId)
        {
            return userId < cache.Length ? cache[userId].GetVerifyCode(sessionId) : 0;
        }
        /// <summary>
        /// 删除答案缓存
        /// </summary>
        /// <param name="userId">考试用户标识</param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void remove(int userId)
        {
            Func<fastCSharp.net.returnValue<int>, bool> onChange = null;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length)
            {
                onChange = cache[userId].Remove();
                Monitor.Exit(cacheLock); 
                this.onChange(onChange, -1);
                return;
            }
            try
            {
                create(userId);
                cache[userId].SessionId = -1;
            }
            finally { Monitor.Exit(cacheLock); }
        }
        /// <summary>
        /// 删除答案缓存
        /// </summary>
        /// <param name="userIds">考试用户标识</param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void remove(int[] userIds)
        {
            int maxUserId = userIds.max(0), onChangeCount = 0;
            Func<fastCSharp.net.returnValue<int>, bool>[] onChanges = new Func<fastCSharp.net.returnValue<int>, bool>[userIds.Length];
            Monitor.Enter(cacheLock);
            if (maxUserId < cache.Length)
            {
                foreach (int userId in userIds) onChanges[onChangeCount++] = cache[userId].Remove();
                Monitor.Exit(cacheLock); 
            }
            else
            {
                try
                {
                    create(maxUserId);
                    foreach (int userId in userIds) onChanges[onChangeCount++] = cache[userId].Remove();
                }
                finally { Monitor.Exit(cacheLock); }
            }
            foreach (Func<fastCSharp.net.returnValue<int>, bool> onChange in onChanges) this.onChange(onChange, -1);
        }
        /// <summary>
        /// 删除答案缓存
        /// </summary>
        /// <param name="userId">考试用户标识</param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false)]
        private void reset(int userId)
        {
            Func<fastCSharp.net.returnValue<int>, bool> onChange = null;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length) onChange = cache[userId].Null();
            Monitor.Exit(cacheLock); 
            this.onChange(onChange, 0);
        }
        /// <summary>
        /// 用户会话变化通知
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sessionId"></param>
        /// <param name="onChange"></param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousTask = false, IsInputSerializeReferenceMember = false, IsOutputSerializeReferenceMember = false, IsServerAsynchronousCallback = true, IsClientAsynchronous = true, IsClientSynchronous = false)]
        private void sessionChange(int userId, int sessionId, Func<fastCSharp.net.returnValue<int>, bool> onChange)
        {
            Interlocked.Increment(ref userCount);
            Func<fastCSharp.net.returnValue<int>, bool> oldOnChange = null;
            Monitor.Enter(cacheLock);
            if (userId < cache.Length) cache[userId].SetOnChange(ref sessionId, ref onChange, ref oldOnChange);
            Monitor.Exit(cacheLock); 
            this.onChange(onChange, sessionId);
            this.onChange(oldOnChange, sessionId);
        }
        /// <summary>
        /// 用户会话变化通知
        /// </summary>
        /// <param name="onChange"></param>
        /// <param name="sessionId"></param>
        private void onChange(Func<fastCSharp.net.returnValue<int>, bool> onChange, int sessionId)
        {
            if (onChange != null)
            {
                Interlocked.Decrement(ref userCount);
                onChange(sessionId);
            }
        }
        /// <summary>
        /// 保存缓存文件
        /// </summary>
        internal void Save()
        {
            subArray<fileCache> fileCache = default(subArray<fileCache>);
            subArray<keyValue<Func<fastCSharp.net.returnValue<int>, bool>, int>> onChanges = new subArray<keyValue<Func<fastCSharp.net.returnValue<int>, bool>, int>>();
            int userId = 0;
            Monitor.Enter(cacheLock);
            try
            {
                foreach (answerSession answer in cache)
                {
                    if (answer.SessionId != 0)
                    {
                        fileCache.Add(new fileCache { UserId = userId, SessionId = answer.SessionId, VerifyCode = answer.VerifyCode, LogProblemIds = answer.LogProblemIds.Count == 0 ? null : answer.LogProblemIds.ToArray(), IsLog = answer.IsLog, Answers = answer.Answers == null ? null : answer.Answers.Values.getArray() });
                    }
                    if (answer.OnChange != null)
                    {
                        onChanges.Add(new keyValue<Func<fastCSharp.net.returnValue<int>, bool>, int> { Key = answer.OnChange, Value = answer.SessionId });
                    }
                    ++userId;
                }
            }
            finally { Monitor.Exit(cacheLock); }
            if (fileCache.Count == 0) File.Delete(cacheFileName);
            else File.WriteAllBytes(cacheFileName, fastCSharp.emit.dataSerializer.Serialize(fileCache.ToArray()));
            foreach (keyValue<Func<fastCSharp.net.returnValue<int>, bool>, int> onChange in onChanges) onChange.Key(onChange.Value);
        }
    }
}
